/*
 * CommandTokenizer.java
 *
 * Created on 14 novembre 2007, 13:00
 */
package com.reimscopie.console ;

import java.io.* ;
import java.util.* ;


/**
 * This string tokenizer is designed to extract command line words.
 */
class CommandTokenizer implements Enumeration<String> {
  
  // input reader
  private Reader reader ;
  
  // next token
  private String token ;
  
  
  /**
   * Create a new <code>CommandTokenizer</code> instance.
   *
   * @param reader The whole command line.
   *
   * @exception IOException An error occurs while reading the first token.
   */
  CommandTokenizer(Reader reader) throws IOException {
    
    this.reader = reader ;
    
    readToken() ;
    
  }
  
  
  /**
   * Returns if this enumeration contains more elements.
   *
   * @return <code>true</code> if there is still a word.
   *
   * @see #hasMoreTokens
   */
  public boolean hasMoreElements() {
    
    return hasMoreTokens() ;
    
  }
  
  
  /**
   * Tests if a token is still available.
   *
   * @return <code>true</code> if there is more tokens.
   */
  boolean hasMoreTokens() {
    
    return token != null ;
    
  }
  
  
  /**
   * Returns the next element of this enumeration if this enumeration object
   * has at least one more element to provide.
   *
   * @return The new element.
   *
   * @exception NoSuchElementException If no more elements exist.
   *
   * @see #nextToken
   */
  public String nextElement() throws NoSuchElementException {
    
    try {
      return nextToken() ;
    }
    catch (IOException e) {
      throw new NoSuchElementException(e.getMessage()) ;
    }
      
  }
  
  
  /**
   * Returns the next token from this tokenizer.
   *
   * @return The next token.
   *
   * @exception NoSuchElementException If there are no more tokens in this
   * tokenizer's string.
   * @exception IOException Ann error occurs in the input reader.
   */
  String nextToken() throws NoSuchElementException,IOException {
    
    if (token != null) {
      String oldToken = token ;
      readToken() ;
      return oldToken ;
    }
    
    else
      throw new NoSuchElementException() ;
    
  }
  
  
  /**
   * Read the next token in the input reader.
   *
   * @exception IOException An error occurs in the input reader.
   */
  private void readToken() throws IOException {
    
    StringBuilder builder = new StringBuilder() ;
    
    int character ;
    int quote = -1 ;
    
    while ((character = reader.read()) > 0) {
      
      // escape
      if (character == '\\') {
        character = reader.read() ;
        if (character <= 0)
          throw new IOException("illegal escape") ;
        builder.appendCodePoint(character) ;
        continue ;
      }
      
      // quote
      if (character == quote) {
        quote = -1 ;
        continue ;
      }
      if (quote != -1) {
        builder.appendCodePoint(character) ;
        continue ;
      }
      if (character == '"'  ||  character == '\'') {
        quote = character ;
        continue ;
      }
      
      // white
      if (Character.isWhitespace(character)  &&  quote == -1) {
        if (builder.length() > 0)
          break ;
        else
          continue ;
      }
      
      builder.appendCodePoint(character) ;
    }
    
    
    if (quote != -1)
      throw new IOException("unclosed quote") ;
    
    if (builder.length() > 0)
      token = builder.toString() ;
    else
      token = null ;
    
  }

}
